{% import "types_forward_declarations.inc.jinja" as types_forward_declarations %}
{% import "utils.inc.jinja" as utils %}

{% macro render_gen_node(node) %}
{% if node.kind == "namespace" %}
{{ do_render_namespace(node) -}}
{% elif node.kind == "struct" %}
{{ do_render_struct(node) -}}
{% elif node.kind == "enum" %}
{{ do_render_enum(node) -}}
{% elif node.kind == "oneof" %}
{{ do_render_oneof(node) -}}
{% elif node.kind == "vanilla_class" %}
{% else %}
NOT_IMPLEMENTED_ERROR()
{% endif %}
{% endmacro %}

{% macro do_render_namespace(namespace) %}

{% call utils.optional_namespace(namespace.short_name) %}
{% call utils.prepend_empty_line_if_not_empty() %}
{% for child in namespace.children %}
{{ types_forward_declarations.render_gen_node(child) -}}
{% endfor %}
{% endcall %}
{% for child in namespace.children %}
{{ render_gen_node(child) -}}
{% endfor %}

{% endcall %}  {#- optional namespace -#}
{% endmacro %}

{% macro do_render_struct(struct) %}

struct {{ struct.name.short_name }} final {
{% filter indent(width=4, first=True) %}
using ProtobufMessage = ::{{ struct.vanilla_full_name_fwd }};
{% call utils.prepend_empty_line_if_not_empty() %}
{% for nested in struct.nested_types %}
{{ types_forward_declarations.render_gen_node(nested) -}}
{% endfor %}
{% endcall %}  {#- prepend empty line -#}
{% for nested in struct.nested_types %}
{{ render_gen_node(nested) -}}
{% endfor %}
{% call utils.prepend_empty_line_if_not_empty() %}
{% for field in struct.fields %}
{{ do_render_field(struct, field) -}}
{% endfor %}
{% endcall %}  {#- prepend empty line -#}
{% endfilter %}  {#- indent #}
};
{% endmacro %}

{% macro do_render_enum(enum) %}

enum class {{ enum.name.short_name }} : std::int32_t {
{% filter indent(width=4, first=True) %}
{% for value in enum.values %}
{{ value.short_name }} = {{ value.number }},
{% endfor %}
INTERNAL_DO_NOT_USE_ = std::numeric_limits<std::int32_t>::max(),  // Write default: instead.
{% endfilter %}
};
{% endmacro %}

{% macro do_render_oneof(oneof) %}

struct {{ oneof.name.short_name }} final : {{ type_ref_consts.ONEOF_BASE_CLASS.contextual_cpp_name(context=oneof) }}<
{% filter indent(width=4, first=True) %}
{% for field in oneof.fields %}
{{ field.field_type.contextual_cpp_name(context=oneof) }}
{%- if not loop.last -%}
,
{%- endif %}

{% endfor %} {#- for field #}
{% endfilter %} {#- filter indent (template args) #}
> {
{% filter indent(width=4, first=True) %}
UPROTO_ONEOF_HEADER({{ oneof.name.short_name }})

// For each field {name}, there are methods: has_{name}, {name} (const& and &&), set_{name}, mutable_{name}.
{% for field in oneof.fields %}
UPROTO_ONEOF_FIELD({{ oneof.name.short_name }}, {{ field.field_type.contextual_cpp_name(context=oneof) }}, {{ field.short_name }})
{% endfor %} {#- for field #}
{% endfilter %} {#- filter indent (struct contents) #}
};
{% endmacro %} {#- do_render_oneof -#}

{% macro do_render_field(struct, field) %}
{{ field.field_type.contextual_cpp_name(context=struct) }} {{ field.short_name }}{{ field.initializer }};
{% endmacro %}
